#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>

// 这里就使用管道pipe()函数来进行一个简单测试
// 让子进程写，父进程读 -> 同步写入

// 读快写慢 读慢写快的测试
//
/* int main(int argc, char* argv[])
{
    int pipeid[2] = {0};
    // 1. 父进程创建管道
    if (pipe(pipeid) == -1)
    {
        std::cout << "pipe create ERROR!" << std::endl;
        return 1;
    }

    // 数组的0下标位置是读端，1下标是写端
    // 此时该进程(父进程)分别使用读写两个方式打开了一个管道文件，也就是有两个描述符

    // 2. 创建子进程
    pid_t subid = fork();
    if (subid < 0)
    {
        std::cout << "fork ERROR!" << std::endl;
        exit(errno);
    }
    else if (subid == 0)
    {
        // 3. Child Process -> 关闭读端
        close(pipeid[0]);
        // 写入
        int cnt = 0;
        while (1)
        {
            char buffer[1024] = {'\0'};
            snprintf(buffer, sizeof(buffer), "%d : i am a child Process, pid is %d\n", cnt++, getpid());
            write(pipeid[1], buffer, strlen(buffer));
            //sleep(1); //不让子进程写的时候睡眠，就会导致不断往里面写，让读端每个5s读一次
            sleep(3); //写的慢 会导致read端阻塞等待！
        }
        close(pipeid[1]);
        exit(0);
    }
    else
    {
        // 3. Father Process -> 关闭写端
        close(pipeid[1]);
        // 读取
        while (1)
        {
            //sleep(5);
            char buffer[1025] = {'\0'};
            read(pipeid[0], buffer, sizeof(buffer));
            std::cout << "i am a father Process, my Child says -> " << buffer << std::endl;
            //sleep(1);
        }
        close(pipeid[0]);
        waitpid(subid, nullptr, 0);
    }
    return 0;
} */

// 写端不写 读端还在读  -> 操作系统会把写端的进程kill!
// 为了验证这个情况 所以才安排让子进程写 父进程读，然后让父进程等待子进程退出并回收
/* int main(int argc, char *argv[])
{
    int pipeid[2] = {0};
    // 1. 父进程创建管道
    if (pipe(pipeid) == -1)
    {
        std::cout << "pipe create ERROR!" << std::endl;
        return 1;
    }

    // 数组的0下标位置是读端，1下标是写端
    // 此时该进程(父进程)分别使用读写两个方式打开了一个管道文件，也就是有两个描述符

    // 2. 创建子进程
    pid_t subid = fork();
    if (subid < 0)
    {
        std::cout << "fork ERROR!" << std::endl;
        exit(errno);
    }
    else if (subid == 0)
    {
        // 3. Child Process -> 关闭读端
        close(pipeid[0]);
        // 写入一次就停止
        int cnt = 0;
        char buffer[1024] = {0};
        snprintf(buffer, sizeof(buffer), "%d : i am child Process, pid is %d\n", cnt++, getpid());
        write(pipeid[1], buffer, strlen(buffer));

        // 写完一次马上就关写端
        close(pipeid[1]);
        exit(23);
    }
    else
    {
        // 3. Father Process -> 关闭写端
        close(pipeid[1]);
        // 读取 -> 一直读
        int i = 0;
        while (1)
        {
            char buffer[1025] = {'\0'};
            int rid = read(pipeid[0], buffer, sizeof(buffer));
            // 其实是读不到数据，read的返回值是0！所以这里可以做一个判断：
            if(rid == 0){
                std::cout << "没有数据了！" << std::endl;
                return 1;
            }
            std::cout << "第" << i << "次读取："<< "Child says -> " << buffer << std::endl;
            i++;
            sleep(1);
        }
        close(pipeid[0]);
        waitpid(subid, nullptr, 0);
    }
    return 0;
} */

// 读端不读 写端继续写
int main(int argc, char *argv[])
{
    int pipeid[2] = {0};
    // 1. 父进程创建管道
    if (pipe(pipeid) == -1)
    {
        std::cout << "pipe create ERROR!" << std::endl;
        return 1;
    }

    // 数组的0下标位置是读端，1下标是写端
    // 此时该进程(父进程)分别使用读写两个方式打开了一个管道文件，也就是有两个描述符

    // 2. 创建子进程
    pid_t subid = fork();
    if (subid < 0)
    {
        std::cout << "fork ERROR!" << std::endl;
        exit(errno);
    }
    else if (subid == 0)
    {
        // 3. Child Process -> 关闭读端
        close(pipeid[0]);
        // 写端 -> 一直尝试写入

        int cnt = 0;
        while (1)
        {
            char buffer[1024] = {0};
            snprintf(buffer, sizeof(buffer), "%d -> i am child Process, pid is %d", cnt++, getpid());
            write(pipeid[1], buffer, strlen(buffer));
            std::cout << "子进程 : 完成第" << cnt << "次写入!" << std::endl;
        }

        close(pipeid[1]);
        exit(23);
    }
    else
    {
        // 3. Father Process -> 关闭写端
        close(pipeid[1]);
        // 读取 -> 只读一次

        char buffer[1025] = {'\0'};
        int rid = read(pipeid[0], buffer, sizeof(buffer));
        // 其实是读不到数据，read的返回值是0！所以这里可以做一个判断：
        if (rid == 0)
        {
            std::cout << "没有数据了！" << std::endl;
            return 1;
        }
        std::cout << "父进程 : 只读取一次 ->" << "Child says " << buffer << std::endl;
        close(pipeid[0]);

        std::cout << "父进程 : 沉睡5s" << std::endl;
        sleep(5);
        // 可以在这个期间查看到子进程属于是 zombie状态！
        int status;
        int wid = waitpid(subid, &status, 0);
        if(wid == -1){
            std::cout << "父进程 : 回收子进程错误!" << std::endl;
            return errno;
        }
        else{
            // 打印一下对应的信息码：
            std::cout << "父进程 : 退出码 -> " << ((status >> 8) & 0xFF) << std::endl;
            std::cout << "父进程 : 收到信号 -> " << (status & 0x7F) << std::endl;//是 13信号
            if((status & 0x7F) != 0) std::cout << "退出码无意义！" << std::endl;
        }
    }
    return 0;
}
